package com.vae.order.config;

import com.alibaba.fastjson.JSON;
import com.vae.domain.MqMessage;
import com.vae.service.MqMessageService;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ的配置类
 * 生产者: 负责创建队列、交换机，负责消费确认机制(该类要实现ApplicationContextAware接口)的代码编写。不需要写监听的代码，消费者才需要负责监听
 * 为什么消费者和生产者都需要负责创建一模一样的队列、交换机: 因为避免启动顺序导致队列、交换机还没有被创建出来导致的报错。不用担心重复创建
 */
@Configuration
public class MQNotifyConfig implements ApplicationContextAware {

    //交换机名称
    public static final String PAYNOTIFY_EXCHANGE_FANOUT = "paynotify_exchange_fanout";
    //消息类型
    public static final String MESSAGE_TYPE = "payresult_notify";
    //队列名称
    public static final String PAYNOTIFY_QUEUE = "paynotify_queue";

    //交换机，且持久化
    @Bean(PAYNOTIFY_EXCHANGE_FANOUT)
    public FanoutExchange paynotify_exchange_fanout() {
        // 三个参数：交换机名称、是否持久化、当没有queue与其绑定时是否自动删除
        return new FanoutExchange(PAYNOTIFY_EXCHANGE_FANOUT, true, false);
    }
    //队列,且持久化
    @Bean(PAYNOTIFY_QUEUE)
    public Queue course_publish_queue() {
        return QueueBuilder.durable(PAYNOTIFY_QUEUE).build();
    }

    //交换机和队列的绑定
    @Bean
    public Binding binding_course_publish_queue(@Qualifier(PAYNOTIFY_QUEUE) Queue queue, @Qualifier(PAYNOTIFY_EXCHANGE_FANOUT) FanoutExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange);
    }

    //消费确认机制
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取RabbitTemplate
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        // 消息处理service
        MqMessageService mqMessageService = applicationContext.getBean(MqMessageService.class);
        // 设置ReturnCallback。发送失败的情况
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            // 交换机 -> 队列
            System.out.println("消息发送失败，应答码: "+replyCode+"。原因: "+replyText+"。交换机: "+exchange+"。路由键: "+routingKey+"。消息: "+ message);
            MqMessage mqMessage = JSON.parseObject(message.toString(), MqMessage.class);
            // 兜底。将这个失败的消息再添加到消息表。原理: 如果消息发送成功，那么消息就会从消息表删除，如果消息发送失败，那么消息会重新回到消息表
            mqMessageService.addMessage(mqMessage.getMessageType(),mqMessage.getBusinessKey1(),mqMessage.getBusinessKey2(),mqMessage.getBusinessKey3());

        });
    }
}